import mimetypes
import os
import smtplib
import time
import zipfile
from datetime import datetime
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from config.constants import Constants
from config.mail_config import MailConfig

# 邮件工具类
class MailUtil:
    def __init__(self):
        # 发送邮件，需要第三方邮件的smtp服务器，密码是在邮箱网站申请授权码，不是登录密码
        self.mail_host = MailConfig.mail_host
        self.mail_port = MailConfig.mail_port
        self.mail_user = MailConfig.mail_user
        self.mail_psw = MailConfig.mail_psw

        # 收件人邮箱地址，[]包裹，可以写多个邮件地址群发
        self.receiver_list = MailConfig.receiver_list
        # 抄送人邮箱地址，[]包裹，可以写多个邮件地址群发
        self.copy_list = MailConfig.copy_list

        # 巡检人员邮箱信息：默认 mail_user
        self.inspect_user = MailConfig.inspect_user

    # 设置邮件公共的必传信息
    # 发送时smtp.sendmail再次传入To和Cc信息，若需要修改，需要在setMailCommon之前修改self对应属性
    def setMailCommon(self, subject, content):
        """
        :param subject: 邮件主题
        :param content: 邮件内容
        :return: 邮件主体 MIMEMultipart
        """
        # 添加一个MIMEMultipart类，处理正文及附件
        message = MIMEMultipart()
        # 邮件主题
        message['Subject'] = subject
        # 发件人
        message['From'] = self.mail_user
        # 收件人
        message['To'] = (','.join(self.receiver_list))
        # 抄送人
        message['Cc'] = (','.join(self.copy_list))

        # 添加邮件内容，plain 表示纯文本
        content = MIMEText(content, 'plain', 'utf-8')
        # 将内容加到邮件主体中
        message.attach(content)
        return message

    # 登录qq邮箱，发送邮件
    def send(self, subject, message):
        """
        :param subject: 邮件主题
        :param message: 邮件主体 MIMEMultipart
        """
        send_success = False
        try:
            # QQ邮箱SMTP服务器smtp.qq.com，SSL一般用465
            smtp = smtplib.SMTP_SSL(self.mail_host, self.mail_port)
            # 打印出和SMTP服务器交互的所有信息
            # smtp.set_debuglevel(1)
            # 登录到服务器
            smtp.login(self.mail_user, self.mail_psw)
            # 发送
            smtp.sendmail(self.mail_user, self.receiver_list + self.copy_list, message.as_string())
            # 退出
            smtp.quit()
            send_success = True
        except Exception as e:
            # 打印错误
            print(subject + '邮件发送失败', e)
        finally:
            return send_success

    # 发送邮件：网络问题或其它问题时，需要重发
    def sendMail(self,subject, message):
        send_success = self.send(subject, message)
        if not send_success:
            # 当邮件发送失败时，需要间隔resend_mail_internal秒重新发送，重试resend_mail_count次
            print("%s邮件发送失败，%s 秒后重新发送，重试 %s 次>>>" % (subject,Constants.RESEND_MAIL_INTERNAL, Constants.RESEND_MAIL_COUNT))
            send_count = 1
            while send_count <= Constants.RESEND_MAIL_COUNT:
                # 等待定义的间隔时间后重试
                time.sleep(Constants.RESEND_MAIL_INTERNAL)
                # 发送带压缩报告目录的邮件
                print("%s邮件重新发送，第 %s 次重试..." % (subject, send_count))
                send_success = self.send(subject, message)
                if send_success:
                    print("%s邮件重发成功！<<<" % subject)
                    break
                if send_count < Constants.RESEND_MAIL_COUNT:
                    print("%s邮件发送失败，%s 秒后重新发送..." % (subject, Constants.RESEND_MAIL_INTERNAL))
                send_count += 1
            # 邮件发送重试结束
            if not send_success:
                print("安卓自动化测试报告邮件发送失败，请检查邮箱配置信息和网络设置！<<<")
        else:
            print("%s邮件发送成功！" % subject)
        return send_success

    # 发送纯文本邮件
    def sendTextMail(self, subject, content):
        """
        :param subject: 邮件主题
        :param content: 邮件内容
         """
        message = self.setMailCommon(subject, content)
        self.sendMail(subject, message)

    # 发送带报告附件邮件
    def sendReportMail(self, subject, content, html_path):
        """
        :param subject: 邮件主题
        :param content: 邮件内容
        :param html_path:html附件路径
         """
        message = self.setMailCommon(subject, content)

        # 添加报告附件
        with open(html_path, 'r', encoding='UTF-8') as report:
            report = report.read()
        # 设置附件参数
        attachment = MIMEText(report, 'html', 'utf-8')
        # 附件设置内容类型，设置为二进制流
        attachment['Content-Type'] = 'application/octet-stream'
        # 设置附件头，添加文件名
        attachment['Content-Disposition'] = 'attachment;filename="report.html"'
        # 将附件加到邮件主体中
        message.attach(attachment)
        self.sendMail(subject, message)

    # 压缩指定文件夹
    def zipDir(self, path_dir, path_zip):
        """
        :param path_dir: 目标文件夹路径
        :param path_zip: 压缩文件保存路径+xxxx.zip
        """
        zip = zipfile.ZipFile(path_zip, "w", zipfile.ZIP_DEFLATED)
        for path, dirnames, filenames in os.walk(path_dir):
            # 去掉目标跟路径，只对目标文件夹下边的文件及文件夹进行压缩
            fpath = path.replace(path_dir, '')

            for filename in filenames:
                zip.write(os.path.join(path, filename), os.path.join(fpath, filename))
        zip.close()
        print("压缩成功")

    # 发送zip压缩附件邮件
    def sendAttachMail(self, subject, content, file_path):
        """
        :param subject: 邮件主题
        :param content: 邮件内容
        :param file_path:附件路径
         """
        message = self.setMailCommon(subject, content)
        data = open(file_path, 'rb')
        ctype, encoding = mimetypes.guess_type(file_path)
        if ctype is None or encoding is not None:
            ctype = 'application/octet-stream'
        maintype, subtype = ctype.split('/', 1)
        attach_zip = MIMEBase(maintype, subtype)
        attach_zip.set_payload(data.read())
        data.close()
        encoders.encode_base64(attach_zip)  # 把附件编码
        # 修改附件名称
        time = datetime.today().strftime('%Y{y}%m{m}%d{d} %H{h}%M{f}%S{s}').format(y='年', m='月', d='日', h='时', f='分',
                                                                                   s='秒')
        filename = "{}-测试报告.zip".format(time)
        attach_zip.add_header('Content-Disposition', 'attachment', filename=('gbk', '', filename))
        message.attach(attach_zip)

        return self.sendMail(subject, message)

    def sendDeviceDisconnectedMail(self):
        """
        发送设备未连接邮件通知：只发送给巡检人员
        """
        # 发送给当前巡检人员
        self.receiver_list = [MailConfig.inspect_user]
        # 清空抄送列表
        self.copy_list = []

        subject = "安卓自动化测试通知"
        content = "无法进行自动化测试，请检查手机连接状态"
        message = self.setMailCommon(subject, content)

        self.sendMail(subject, message)